﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __COMMON_PACKET_H__
#define __COMMON_PACKET_H__

#pragma once

#include "packet.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 信令定义
enum PAK_EVENT_COMMON
{
    PAKE_LIVE = CNETTraits::EVENT_RESERVED, // 心跳包, CTPAKHeadBase<PAKE_LIVE, PAK_TYPE>
    PAKE_LIVEACK,         // 心跳包Ack, CTPAKHeadBase<PAKE_LIVEACK, PAK_TYPE>
    // 服务器同步消息信令
    PAKE_LINK,            // 注册, CPAKLink
    PAKE_LINKACK,         // 注册Ack, CPAKHeadBase<PAKE_LINKACK, PAK_TYPE>

    PAKE_UPDATE,          // 更新, CPAKUpdate
    PAKE_UPDATEACK,       // 更新Ack, CPAKHeadBase<PAKE_UPDATEACK, PAK_TYPE>

    PAKE_SYNC,            // 同步, CPAKSync
    PAKE_SYNCACK,         // 同步Ack, CPAKHeadBase<PAKE_SYNCACK, PAK_TYPE>

    PAKE_UNLINK,          // 注销, CPAKHeadBase<PAKE_UNLINK, PAK_TYPE>
    PAKE_UNLINKACK,       // 注销Ack, CPAKHeadBase<PAKE_UNLINKACK, PAK_TYPE>

    // 服务器向例程传递网路层动作的事件定义
    PAKE_TCP_ACCEPT,
    PAKE_TCP_CONNECT,
    PAKE_TCP_CLOSED,

    PAKE_UDP_RECVED,
    PAKE_UDP_CLOSED,
    // 事件队列初始化时队列线程OnStart和OnStop调用转换成事件通知handler
    PAKE_THREAD_START,
    PAKE_THREAD_STOP,
    // 扩展配置初始化加载和更新加载
    PAKE_CONFIG_DEFAULT,
    PAKE_CONFIG_LOAD,
    PAKE_CONFIG_UPDATE,
    PAKE_CONFIG_LINK,
};

enum PAK_STREAM_TYPE
{
    PAK_STREAM_NONE,
    PAK_STREAM_ATTACH,
    PAK_STREAM_ALLOC,
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 会话
class CPAKSessionBase : public CPAKHeadBase
{
public:
    CPAKSessionBase(UEVENT uEvent = PAKE_LIVEACK, UTYPE uType = PAKT_NONE, UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CPAKSessionBase(void) OVERRIDE;

    CPAKSessionBase(const CPAKSessionBase& aSrc);
    CPAKSessionBase& operator=(const CPAKSessionBase& aSrc);

    virtual size_t Length(void) OVERRIDE;
    virtual void   Serialize(CStream&) OVERRIDE;
    /// 获取Session值
    ULLong         GetSessionId(void);
    /// 设置Session值
    void           SetSessionId(ULLong ullSessionId);
private:
    ULLong         m_ullSessionId;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 会话模板
template <UEVENT uEvent, UTYPE uType>
class CTPAKSessionBase : public CTPAKHeadBase<uEvent, uType>
{
public:
    CTPAKSessionBase(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKSessionBase(void) OVERRIDE;

    CTPAKSessionBase(const CTPAKSessionBase& aSrc);
    CTPAKSessionBase& operator=(const CTPAKSessionBase& aSrc);

    virtual size_t Length(void) OVERRIDE;
    virtual void   Serialize(CStream&) OVERRIDE;
    /// 获取Session值
    ULLong         GetSessionId(void);
    /// 设置Session值
    void           SetSessionId(ULLong ullSessionId);
private:
    ULLong         m_ullSessionId;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 业务
class CPAKRoutineBase : public CPAKSessionBase
{
public:
    CPAKRoutineBase(UEVENT uEvent = PAKE_ROUTINE_BEGIN, UTYPE uType = PAKT_EXTERNAL_MASTER, UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CPAKRoutineBase(void) OVERRIDE;

    CPAKRoutineBase(const CPAKRoutineBase& aSrc);
    CPAKRoutineBase& operator=(const CPAKRoutineBase& aSrc);

    virtual size_t  Length(void) OVERRIDE;
    virtual void    Serialize(CStream&) OVERRIDE;

    ULLong          GetRoutineId(void);
    void            SetRoutineId(ULLong ullRoutineId);
private:
    ULLong          m_ullRoutineId;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 业务模板
template <UEVENT uEvent, UTYPE uType>
class CTPAKRoutineBase : public CTPAKSessionBase<uEvent, uType>
{
public:
    CTPAKRoutineBase(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKRoutineBase(void) OVERRIDE;

    CTPAKRoutineBase(const CTPAKRoutineBase& aSrc);
    CTPAKRoutineBase& operator=(const CTPAKRoutineBase& aSrc);

    virtual size_t  Length(void) OVERRIDE;
    virtual void    Serialize(CStream&) OVERRIDE;

    ULLong          GetRoutineId(void);
    void            SetRoutineId(ULLong ullRoutineId);
private:
    ULLong          m_ullRoutineId;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 通用数据流模板TPAKBase : CPAKHeadBase, CPAKSessionBase, CPAKRoutineBase或者继承类
template <typename TPAKBase>
class CTPAKStream : public TPAKBase
{
public:
    CTPAKStream(UEVENT uEvent = PAKE_LIVE, UTYPE uType = PAKT_NONE, UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKStream(void) OVERRIDE;

    CTPAKStream(const CTPAKStream& aSrc);
    CTPAKStream& operator=(const CTPAKStream& aSrc);

    virtual size_t Length(void) OVERRIDE;
    virtual void   Serialize(CStream&) OVERRIDE;

    virtual UInt   GetStreamType(void);
    /// 检测数据流是否存在
    bool       CheckStream(void);
    /// 获取数据流
    CStream&   GetStream(void);
protected:
    /// 设置数据流
    void       SetStream(CStream& Stream);
private:
    CStream*   m_pStream;      ///< 本地数据, 网络传输序列化时忽略大小
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 通用数据流模板TPAKBase : CTPAKHeadBase, CTPAKSessionBase, CTPAKRoutineBase或者继承类
template <typename TPAKBaseT>
class CTPAKStreamT : public TPAKBaseT
{
public:
    CTPAKStreamT(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKStreamT(void) OVERRIDE;

    CTPAKStreamT(const CTPAKStreamT& aSrc);
    CTPAKStreamT& operator=(const CTPAKStreamT& aSrc);

    virtual size_t Length(void) OVERRIDE;
    virtual void   Serialize(CStream&) OVERRIDE;

    virtual UInt   GetStreamType(void);
    /// 检测数据流是否存在
    bool       CheckStream(void);
    /// 获取数据流
    CStream&   GetStream(void);
protected:
    /// 设置数据流
    void       SetStream(CStream& Stream);
private:
    CStream*   m_pStream;      ///< 本地数据, 网络传输序列化时忽略大小
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NET缓存数据流模板TPAKBase : CPAKHeadBase, CPAKSessionBase, CPAKRoutineBase或者继承类
/// m_Index + m_pCache from MObject Cache
template <typename TPAKBase>
class CTPAKNETStream : public CTPAKStream<TPAKBase>
{
public:
    CTPAKNETStream(UEVENT uEvent = PAKE_LIVE, UTYPE uType = PAKT_NONE, UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKNETStream(void) OVERRIDE;

    CTPAKNETStream(const CTPAKNETStream& aSrc);
    CTPAKNETStream& operator=(const CTPAKNETStream& aSrc);

    virtual size_t  Length(void) OVERRIDE;
    virtual void    Serialize(CStream&) OVERRIDE;

    virtual UInt    GetStreamType(void) OVERRIDE;
    // PAK_STREAM_ATTACH, 返回读数据流, 已经跳过m_uBaseSize
    CBufStreamBase& Attach(ULLong ullParam, QUEUE_TYPE eType, CNetworkPtr& NetworkPtr);
    PByte           Detach(PINDEX& index);
    // PAK_STREAM_ALLOC, 返回写数据流, 已经跳过m_uBaseSize
    CBufStreamBase& Alloc(CNetworkPtr& NetworkPtr, bool bJumbo = false);

    // 获取写数据流, sPos为获取写数据流时的从开始跳转大小, sPos=0表示从开始跳转m_uBaseSize
    CBufStreamBase& WriteStream(CNetworkPtr& NetworkPtr, SeekPos sPos = 0);
    // 获取读数据流, sPos为获取读数据流时的从开始跳转大小, sPos=0表示从开始跳转m_uBaseSize
    CBufStreamBase& ReadStream(CNetworkPtr& NetworkPtr, SeekPos sPos = 0);

    // Reset时候会把TPAKBase数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      Reset(SeekPos sPos = 0);
    // Send时候会把TPAKBase数据重新写入要发送的流的开始位置
    bool      Send(Socket_t sSocket, CNetworkPtr& NetworkPtr, bool bClose = false);
    // SendCopy时候会把TPAKBase数据重新写入要发送的流的开始位置, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      SendCopy(Socket_t sSocket, CNetworkPtr& NetworkPtr, bool bClose = false);
    // ResetSend时候会把TPAKBase数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      ResetSend(Socket_t sSocket, CNetworkPtr& NetworkPtr, SeekPos sPos, bool bClose = false);
    // ResetSendCopy时候会把TPAKBase数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      ResetSendCopy(Socket_t sSocket, CNetworkPtr& NetworkPtr, SeekPos sPos, bool bClose = false);

    // BroadcastSend时候会把TPAKBase数据重新写入要发送的流的开始位置
    bool      BroadcastSend(CNetworkPtr& NetworkPtr, Int nFlag, ULLong ullParam);
    // BroadcastSendCopy时候会把TPAKBase数据重新写入要发送的流的开始位置, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      BroadcastSendCopy(CNetworkPtr& NetworkPtr, Int nFlag, ULLong ullParam);
    // ResetBroadcastSend时候会把TPAKBase数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      ResetBroadcastSend(CNetworkPtr& NetworkPtr, SeekPos sPos, Int nFlag, ULLong ullParam);
    // ResetBroadcastSendCopy时候会把TPAKBase数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      ResetBroadcastSendCopy(CNetworkPtr& NetworkPtr, SeekPos sPos, Int nFlag, ULLong ullParam);

    // 返回TPAKBase的大小, 注意, 数据都应该追加到TPAKBase的后面
    size_t    BaseSize(void);
    // Valid = true, 返回m_stCache - m_uBaseSize的大小
    size_t    Cache(bool bValid = true);

    CNetAddr& Addr(void);
    void      Addr(CNetAddr& SetAddr);

    void      Free(void);
private:
    bool      Alloc(PByte pData, size_t stSize, CNetworkPtr& NetworkPtr, bool bJumbo = false);
private:
    UInt              m_uStreamType;
    UInt              m_uBaseSize; // TPAKBase序列化大小
    //
    size_t            m_stCache;   // 整个缓存大小(包含m_uBaseSize, 实际能读写的大小为m_stCache - m_uBaseSize)
    // Attach模式缓存的索引和缓存地址
    PINDEX            m_Index;
    PByte             m_pCache;
    CBufReadStream    m_BufRead;   // 读模式
    //
    CNetStreamPtr     m_StreamPtr; // 通过AllocBuffer/AllocJumboBuffer申请的网络层发送缓存
    CNetStreamPtr     m_ReferPtr;
    //
    CNetAddr          m_NetAddr;   // UDP模式Attach带的目标地址
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// NET缓存数据流模板TPAKBaseT : CTPAKHeadBase, CTPAKSessionBase, CTPAKRoutineBase或者继承类
/// m_Index + m_pCache from MObject Cache
template <typename TPAKBaseT>
class CTPAKNETStreamT : public CTPAKStreamT<TPAKBaseT>
{
public:
    CTPAKNETStreamT(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKNETStreamT(void) OVERRIDE;

    CTPAKNETStreamT(const CTPAKNETStreamT& aSrc);
    CTPAKNETStreamT& operator=(const CTPAKNETStreamT& aSrc);

    virtual size_t  Length(void) OVERRIDE;
    virtual void    Serialize(CStream&) OVERRIDE;

    virtual UInt    GetStreamType(void) OVERRIDE;
    // PAK_STREAM_ATTACH, 返回读数据流, 已经跳过m_uBaseSize
    CBufStreamBase& Attach(ULLong ullParam, QUEUE_TYPE eType, CNetworkPtr& NetworkPtr);
    PByte           Detach(PINDEX& index);
    // PAK_STREAM_ALLOC, 返回写数据流, 已经跳过m_uBaseSize
    CBufStreamBase& Alloc(CNetworkPtr& NetworkPtr, bool bJumbo = false);

    // 获取写数据流, sPos为获取写数据流时的从开始跳转大小, sPos=0表示从开始跳转m_uBaseSize
    CBufStreamBase& WriteStream(CNetworkPtr& NetworkPtr, SeekPos sPos = 0);
    // 获取读数据流, sPos为获取读数据流时的从开始跳转大小, sPos=0表示从开始跳转m_uBaseSize
    CBufStreamBase& ReadStream(CNetworkPtr& NetworkPtr, SeekPos sPos = 0);

    // Reset时候会把TPAKBaseT数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      Reset(SeekPos sPos = 0);
    // Send时候会把TPAKBaseT数据重新写入要发送的流的开始位置
    bool      Send(Socket_t sSocket, CNetworkPtr& NetworkPtr, bool bClose = false);
    // SendCopy时候会把TPAKBaseT数据重新写入要发送的流的开始位置, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      SendCopy(Socket_t sSocket, CNetworkPtr& NetworkPtr, bool bClose = false);
    // ResetSend时候会把TPAKBaseT数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      ResetSend(Socket_t sSocket, CNetworkPtr& NetworkPtr, SeekPos sPos, bool bClose = false);
    // ResetSendCopy时候会把TPAKBaseT数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      ResetSendCopy(Socket_t sSocket, CNetworkPtr& NetworkPtr, SeekPos sPos, bool bClose = false);

    // BroadcastSend时候会把TPAKBaseT数据重新写入要发送的流的开始位置
    bool      BroadcastSend(CNetworkPtr& NetworkPtr, Int nFlag, ULLong ullParam);
    // BroadcastSendCopy时候会把TPAKBaseT数据重新写入要发送的流的开始位置, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      BroadcastSendCopy(CNetworkPtr& NetworkPtr, Int nFlag, ULLong ullParam);
    // ResetBroadcastSend时候会把TPAKBaseT数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize
    bool      ResetBroadcastSend(CNetworkPtr& NetworkPtr, SeekPos sPos, Int nFlag, ULLong ullParam);
    // ResetBroadcastSendCopy时候会把TPAKBaseT数据重新写入要发送的流的开始位置, sPos=0表示从开始跳转m_uBaseSize, 网络层拷贝一份同样数据进行发送, 当前数据保留不动
    bool      ResetBroadcastSendCopy(CNetworkPtr& NetworkPtr, SeekPos sPos, Int nFlag, ULLong ullParam);

    // 返回TPAKBaseT的大小, 注意, 数据都应该追加到TPAKBaseT的后面
    size_t    BaseSize(void);
    // Valid = true, 返回m_stCache - m_uBaseSize的大小
    size_t    Cache(bool bValid = true);

    CNetAddr& Addr(void);
    void      Addr(CNetAddr& SetAddr);

    void      Free(void);
private:
    bool      Alloc(PByte pData, size_t stSize, CNetworkPtr& NetworkPtr, bool bJumbo = false);
private:
    UInt              m_uStreamType;
    UInt              m_uBaseSize; // TPAKBaseT序列化大小
                                   //
    size_t            m_stCache;   // 整个缓存大小(包含m_uBaseSize, 实际能读写的大小为m_stCache - m_uBaseSize)
                                   // Attach模式缓存的索引和缓存地址
    PINDEX            m_Index;
    PByte             m_pCache;
    CBufReadStream    m_BufRead;   // 读模式
                                   //
    CNetStreamPtr     m_StreamPtr; // 通过AllocBuffer/AllocJumboBuffer申请的网络层发送缓存
    CNetStreamPtr     m_ReferPtr;
    //
    CNetAddr          m_NetAddr;   // UDP模式Attach带的目标地址
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 通用数据包
typedef CTPAKNETStream<CPAKHeadBase>       CNETHead;
typedef CTPAKStream<CPAKHeadBase>          CPAKHead;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 通用会话数据包
typedef CTPAKNETStream<CPAKSessionBase>    CNETSession;
typedef CTPAKStream<CPAKSessionBase>       CPAKSession;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 通用业务数据包
typedef CTPAKNETStream<CPAKRoutineBase>    CNETRoutine;
typedef CTPAKStream<CPAKRoutineBase>       CPAKRoutine;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// 服务器状态数据包
template <UEVENT uEvent, UTYPE uType>
class CTPAKServerStatusT : public CTPAKHeadBase<uEvent, uType>
{
public:
    CTPAKServerStatusT(UACK uAck = (UACK)ERRORD_OKAY);
    virtual ~CTPAKServerStatusT(void);

    CTPAKServerStatusT(const CTPAKServerStatusT& aSrc);
    CTPAKServerStatusT& operator=(const CTPAKServerStatusT& aSrc);

    virtual size_t    Length(void) OVERRIDE;
    virtual void      Serialize(CStream&) OVERRIDE;
    /// 获取服务器状态
    SERVER_STATUS&    GetServerStatus(void);
    /// 设置服务器状态
    void              SetServerStatus(SERVER_STATUS& ss);
private:
    SERVER_STATUS     m_ServerStatus; ///< 服务器状态
};

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_NONE>>         CNETLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_NONE>>       CNETUpdate;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_SYNC, PAKT_NONE>>         CNETSync;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_SYNC, PAKT_CENTER>>       CCenterSync;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_SYNC, PAKT_BALANCE>>      CBalanceSync;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_SYNC, PAKT_AUTHOR>>       CAuthorSync;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_BALANCE>>      CBalanceLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_BALANCE>>    CBalanceUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_AUTHORDB>>     CAuthorDBLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_AUTHORDB>>   CAuthorDBUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_AUTHOR>>       CAuthorLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_AUTHOR>>     CAuthorUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_MASTERDB>>     CMasterDBLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_MASTERDB>>   CMasterDBUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_MASTER>>       CMasterLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_MASTER>>     CMasterUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_SERVICE>>      CServiceLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_SERVICE>>    CServiceUpdate;

typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_LINK, PAKT_GATE>>         CGateLink;
typedef CTPAKNETStreamT<CTPAKServerStatusT<PAKE_UPDATE, PAKT_GATE>>       CGateUpdate;

///////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CommonPack : 通用包解析
namespace CommonPack
{
    static CPAKHeadBase* Create(CStream&, UEVENT& uEvent);
    static size_t        StringLen(CString& str);
    static bool          ReadString(CStream& Stream, CString& str, UInt uMaxLen);
    static void          WriteString(CStream& Stream, CString& str);
    static void          WriteString(CStream& Stream, PCXStr psz);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "commonpacket.inl"

#endif // __COMMON_PACKET_H__
